//------------------------------------------------------------------------------
//
// Service B - Sample service used by Service A
//
// Written Jan-2008 by Trevor Taylor, Software Technology, Australia
//
// Updated to MRDS 2.0 Dec-2008
//
//------------------------------------------------------------------------------

using Microsoft.Ccr.Core;
using Microsoft.Dss.Core;
using Microsoft.Dss.Core.Attributes;
using Microsoft.Dss.ServiceModel.Dssp;
using Microsoft.Dss.ServiceModel.DsspServiceBase;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Xml;
using W3C.Soap;
using serviceb = Robotics.ServiceB;


namespace Robotics.ServiceB
{
    /// <summary>
    /// Implementation class for ServiceB
    /// </summary>
    [DisplayName("ServiceB")]
    [Description("The ServiceB Service")]
    [Contract(Contract.Identifier)]
    public class ServiceBService : DsspServiceBase
    {
        
        /// <summary>
        /// _state
        /// </summary>
        // Set an OPTIONAL initial state partner
        // NOTE: If the config file is NOT specified in the manifest, then the
        // file will be created in the MRDS root directory. If it is listed in the
        // manifest, then it will be created in the same directory as the manifest.
        // If you want to specify explicitly where the config file should be
        // located, use ServicePaths.MountPoint as a prefix.
        [InitialStatePartner(Optional = true, ServiceUri = "ServiceB.Config.xml")]
        private ServiceBState _state = new ServiceBState();

        /// <summary>
        /// _main Port
        /// </summary>
        [ServicePort("/serviceb", AllowMultipleInstances=false)]
        private ServiceBOperations _mainPort = new ServiceBOperations();

        /// <summary>
        /// Default Service Constructor
        /// </summary>
        public ServiceBService(DsspServiceCreationPort creationPort) : 
                base(creationPort)
        {
        }
        
        /// <summary>
        /// Service Start
        /// </summary>
        protected override void Start()
        {
            base.Start();
            // Add service specific initialization here.

            // NOTE: You should NOT get in the habit of using Console.WriteLine.
            // Use LogInfo(LogGroups.Console, "...") or LogError() instead.
            // However, for clarity in the output we have used Console.WriteLine
            // extensively in this example.
            Console.WriteLine("ServiceB starting");

            // Make sure that we have an initial state!
            if (_state == null)
            {
                _state = new ServiceBState();
            }
            // Sanity check the values (or initialize them if empty)
            if (_state.Interval <= 0)
                _state.Interval = 1000;

            // Save the state now
            SaveState(_state);

            SpawnIterator(MainLoop);

        }

        // Global flag to allow terminating the Main Loop
        private bool _shutdown = false;

        private IEnumerator<ITask> MainLoop()
        {

            Random r = new Random();
            
            /*
            // Initial version of the code
            while (true)
            {
                // Generate a random number from 0-100 and write it out
                Console.WriteLine("B: " + r.Next(100));

                // Wait a while
                yield return Arbiter.Receive(
                    false,
                    TimeoutPort(1000),
                    delegate(DateTime time)
                    { }
                );
            }
            */

            // Revised version of the code
            // Notice that there is now a flag to exit the loop,
            // the "sensor" value is stored in the state, and
            // the timer interval is read from the state.
            while (!_shutdown)
            {
                // Generate a random number from 0-100 and save it in the state
                _state.SensorValue = r.Next(100);

                Console.WriteLine("B: " + _state.SensorValue);

                // Wait a while
                yield return Arbiter.Receive(
                    false,
                    TimeoutPort(_state.Interval),
                    delegate(DateTime time)
                    { }
                );
            }

        }

        /// <summary>
        /// Get Handler
        /// </summary>
        /// <param name="get"></param>
        /// <returns></returns>
        [ServiceHandler(ServiceHandlerBehavior.Concurrent)]
        public virtual IEnumerator<ITask> GetHandler(Get get)
        {
            if (_state == null)
                // Oops! Return a failure
                // This can happen due to race conditions if another service
                // issues a Get before the Initial State has been defined
                get.ResponsePort.Post(new Fault());
            else
                // Return the state
                get.ResponsePort.Post(_state);
            yield break;
        }

        /// <summary>
        /// Drop Handler
        /// </summary>
        /// <param name="drop"></param>
        /// <returns></returns>
        [ServiceHandler(ServiceHandlerBehavior.Teardown)]
        public virtual IEnumerator<ITask> DropHandler(DsspDefaultDrop drop)
        {
            // Tell the main loop to stop
            _shutdown = true;

            Console.WriteLine("ServiceB shutting down");

            // Make sure you do this or the sender might be blocked
            // waiting for a response. The base handler will send
            // a response for us.
            base.DefaultDropHandler(drop);

            // That's all folks!
            yield break;
        }

        /// <summary>
        /// Set Interval Handler
        /// </summary>
        /// <param name="request">SetIntervalRequest</param>
        /// <returns></returns>
        [ServiceHandler(ServiceHandlerBehavior.Exclusive)]
        public virtual IEnumerator<ITask> SetIntervalHandler(SetInterval request)
        {
            if (_state == null)
                // Oops! Return a failure
                request.ResponsePort.Post(new Fault());
            else
            {
                // Set the interval
                _state.Interval = request.Body.Interval;
                // Return a success response
                request.ResponsePort.Post(DefaultUpdateResponseType.Instance);
            }
            yield break;
        }

    }
}
